package com.wei.service.impl;

import com.wei.common.SysConfigCodeDefinition;
import com.wei.dao.Page;
import com.wei.dao.read.*;
import com.wei.dao.write.PrescriptionOptMapper;
import com.wei.dao.write.SysConfigOptMapper;
import com.wei.dao.write.WindowOptMapper;
import com.wei.entity.*;
import com.wei.entity.dto.ChargeDto;
import com.wei.entity.dto.ChargeResultDto;
import com.wei.entity.dto.MainDto;
import com.wei.entity.dto.PrescriptionDto;
import com.wei.entity.vo.PrescriptionVo;
import com.wei.service.IPrescriptionService;
import com.wei.service.mapstruct.ChargeMapperImpl;
import com.wei.service.mapstruct.PrescriptionMapperImpl;
import com.wei.service.wrapper.RedisCacheWrapper;
import com.wei.utils.ApplicationConstants;
import com.wei.utils.CollectionUtils;
import com.wei.utils.ExceptionUtils;
import com.wei.utils.Stringutils;
import com.wei.utils.date.DateUtils;
import com.wei.utils.uuid.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class PrescriptionServiceImpl implements IPrescriptionService {
    @Autowired
    private PrescriptionOptMapper prescriptionOptMapper;
    @Autowired
    private PrescriptionReadMapper prescriptionReadMapper;
    @Autowired
    private PatientReadMapper patientReadMapper;
    @Autowired
    private PtVisitReadMapper ptVisitReadMapper;
    @Autowired
    private RedisCacheWrapper redisCacheWrapper;
    @Autowired
    private SysConfigReadMapper sysConfigReadMapper;
    @Autowired
    private SysConfigOptMapper sysConfigOptMapper;
    @Autowired
    private WindowReadMapper windowReadMapper;
    @Autowired
    private WindowOptMapper windowOptMapper;

    public boolean insert(Prescription prescription) {
        return prescriptionOptMapper.insert(prescription) > 0;
    }

    public boolean update(Prescription prescription) {
        return prescriptionOptMapper.update(prescription) > 0;
    }

    public boolean delete(Integer id) {
        return prescriptionOptMapper.delete(id) > 0;
    }

    public boolean batchSava(List<Prescription> prescriptionList) {
        return prescriptionOptMapper.batchSava(prescriptionList) > 0;
    }

    public Prescription getById(Integer id) {
        return prescriptionReadMapper.getById(id);
    }

    public Integer totalCount() {
        return prescriptionReadMapper.totalCount();
    }

    public List<Prescription> getAll(Page<Prescription> page) {
        return prescriptionReadMapper.getAll(page);
    }

    public List<MainDto> getPrescriptionDtoList(Map<String, String> map) {
        String ioNo = map.get("ioNo");
        if (Stringutils.isEmpty(ioNo)) {
            throw ExceptionUtils.mpeValidator("%s", "门诊号不能为空!");
        }
        Patient patient = patientReadMapper.getByIoNo(ioNo);
        if (patient == null) {
            throw ExceptionUtils.mpeValidator("%s", "门诊号不存在!");
        }
        map.put("ptId", patient.getPtId().toString());
        List<PrescriptionVo> prescriptionVoList = prescriptionReadMapper.getPrescriptionVoList(map);
        String status = map.get("status");
        if ("1".equals(status)) {
            prescriptionVoList = prescriptionVoList.stream().filter(m -> "1".equals(m.getChargeStatus())).collect(Collectors.toList());
        } else {
            prescriptionVoList = prescriptionVoList.stream().filter(m -> "0".equals(m.getChargeStatus())).collect(Collectors.toList());
        }
        for (PrescriptionVo vo : prescriptionVoList) {
            if ("17".equals(vo.getPrescriptionType()) || "15".equals(vo.getPrescriptionType()) || "16".equals(vo.getPrescriptionType())) {
                vo.setPrescriptionTypeName(vo.getPrescriptionTypeName() + "处方");
                if ("1".equals(status)) {
                    vo.setGuideAddr("请到" + vo.getExecDeptName() + vo.getGuideDeptAddr() + "(" + vo.getWindow() + ")");
                } else {
                    vo.setGuideAddr(ApplicationConstants.EMPTY);
                }
            } else {
                vo.setGuideAddr(vo.getGuideDeptAddr() + vo.getExecDeptName());
            }
        }

        List<PrescriptionDto> dtoList = PrescriptionMapperImpl.INSTANCE.prescriptionVoListToPrescriptionDtoList(prescriptionVoList);
        for (PrescriptionDto dto : dtoList) {
            List<Charge> chargeList = prescriptionReadMapper.getChargeByPrescriptionId(dto.getPrescriptionId());
            List<ChargeDto> chargeDtoList = ChargeMapperImpl.INSTANCE.ChargeListToChargeDtoList(chargeList);
            dto.setChargeDtoList(chargeDtoList);
        }
        List<MainDto> result = new ArrayList<>();
        if ("1".equals(status)) {
            for (PrescriptionDto dto : dtoList) {
                MainDto main = result.stream().filter(mainDto -> mainDto.getMainId().equals(dto.getMainId())).findFirst().orElse(null);
                if (main == null) {
                    main = new MainDto();
                    main.setMainId(dto.getMainId());
                    main.setPrescriptionDeptName(dto.getPrescriptionDeptName());
                    main.setChargeFee(dto.getChargeFee());
                    main.setPrescriptionDrname(dto.getPrescriptionDrname());
                    main.setPrescriptionTime(dto.getPrescriptionTime());
                    main.setPrescriptionDtoList(new ArrayList<>());
                    main.getPrescriptionDtoList().add(dto);
                    result.add(main);
                } else {
                    main.setChargeFee(main.getChargeFee().add(dto.getChargeFee()));
                    main.getPrescriptionDtoList().add(dto);
                }
            }
        } else {
            for (PrescriptionDto dto : dtoList) {
                MainDto main = result.stream().filter(mainDto -> DateUtils.format(mainDto.getPrescriptionTime(), DateUtils.DATE_PATTERN).equals(DateUtils.format(dto.getPrescriptionTime(), DateUtils.DATE_PATTERN))
                        && mainDto.getPrescriptionDeptName().equals(dto.getPrescriptionDeptName())
                        && mainDto.getPrescriptionDrname().equals(dto.getPrescriptionDrname())).findFirst().orElse(null);
                if (main == null) {
                    main = new MainDto();
                    main.setPrescriptionDeptName(dto.getPrescriptionDeptName());
                    main.setChargeFee(dto.getChargeFee());
                    main.setPrescriptionDrname(dto.getPrescriptionDrname());
                    main.setPrescriptionTime(dto.getPrescriptionTime());
                    main.setPrescriptionDtoList(new ArrayList<>());
                    main.getPrescriptionDtoList().add(dto);
                    result.add(main);
                } else {
                    main.setChargeFee(main.getChargeFee().add(dto.getChargeFee()));
                    main.getPrescriptionDtoList().add(dto);
                }
            }
        }
        return result;
    }

    public ChargeResultDto charge(Map<String, String> map) {
        String prescriptionIds = map.get("prescriptionIds");
        if (Stringutils.isEmptyOrWhiteSpace(prescriptionIds)) {
            throw ExceptionUtils.mpeValidator("%s", "收费处方号不能为空!");
        }
        String serialNumber = map.get("serialNumber");
        if (Stringutils.isEmptyOrWhiteSpace(serialNumber)) {
            throw ExceptionUtils.mpeValidator("%s", "支付流水号不能为空!");
        }
        //支付金额
        String chargeFeeStr = map.get("chargeFee");
        BigDecimal chargeFee = new BigDecimal(chargeFeeStr);
        //支付时间
        String chargeTime = map.get("chargeTime");
        //读取配置
        String target = map.get("target");//100013
        if (Stringutils.isEmptyOrWhiteSpace(target)) {
            throw ExceptionUtils.mpeValidator("%s", "来源不能为空!");
        }
        String chargeDeptCode = ApplicationConstants.EMPTY;
        String empCode = ApplicationConstants.EMPTY;
        SysConfig chargeDeptSysCfg = sysConfigReadMapper.getBySysCode(target);
        if (chargeDeptSysCfg != null) {
            String sysStr = chargeDeptSysCfg.getSysStr();
            String[] split = Stringutils.split(sysStr, ApplicationConstants.COMMA);
            if (split != null && split.length > 0) {
                chargeDeptCode = split[0];
                if (split.length > 1) {
                    empCode = split[1];
                }
            }
        }
        //门诊号
        String ioNo = map.get("ioNo");
        Patient patient = patientReadMapper.getByIoNo(ioNo);
        if (patient == null) {
            throw ExceptionUtils.mpeValidator("%s", "病人信息不存在!");
        }
        //查询处方
        String[] strs = Stringutils.split(prescriptionIds, ApplicationConstants.COMMA);
        //校验处方 有没有停止 有没有结算
        List<PrescriptionVo> prescriptionVoList = prescriptionReadMapper.getPrescriptionVoListByIds(strs);
        if (CollectionUtils.isEmpty(prescriptionVoList)) {
            throw ExceptionUtils.mpeValidator("%s", "收费处方号不正确!");
        }
        Integer visitId = prescriptionVoList.get(0).getVisitId();
        PtVisit ptVisit = ptVisitReadMapper.getById(visitId);
        if (ptVisit == null) {
            throw ExceptionUtils.mpeValidator("%s", "就诊信息不存在!");
        }

        BigDecimal total = new BigDecimal(0l);
        List<String> execDeptList = new ArrayList<>();
        for (PrescriptionVo prescriptionVo : prescriptionVoList) {
            if ("0".equals(prescriptionVo.getPrescriptionStatus())) {
                throw ExceptionUtils.mpeValidator("%s", "处方号状态已更新!");
            }
            if (!"0".equals(prescriptionVo.getChargeStatus())) {
                throw ExceptionUtils.mpeValidator("%s", "费用状态已更新!");
            }
            if (!(patient.getPtId() == prescriptionVo.getPtId())) {
                throw ExceptionUtils.mpeValidator("%s", "病人信息不一致!");
            }
            List<Charge> chargeList = prescriptionReadMapper.getChargeByPrescriptionId(prescriptionVo.getPrescriptionId());
            BigDecimal money = new BigDecimal(0l);
            if (!CollectionUtils.isEmpty(chargeList)) {
                for (Charge charge : chargeList) {
                    total =  total.add(charge.getFeeSum());
                    money = money.add(charge.getFeeSum());
                }
            }
            prescriptionVo.setChargeAmount(money);//反更新处方表的金额
            prescriptionVo.setChargeList(chargeList);
            if ("17".equals(prescriptionVo.getPrescriptionType()) || "15".equals(prescriptionVo.getPrescriptionType()) || "16".equals(prescriptionVo.getPrescriptionType())) {
                if (execDeptList.stream().noneMatch(m -> m.equals(prescriptionVo.getExecDeptCode()))) {
                    execDeptList.add(prescriptionVo.getExecDeptCode());
                }
            }
        }
        //核对费用明细金额
        if (!(total.compareTo(chargeFee)==0)) {
            throw ExceptionUtils.mpeValidator("%s", "处方总金额不正确!");
        }
        // lock会一直等待获取到锁
        String uuid = UUID.fastUUID().toString(true);
        redisCacheWrapper.waitLock(SysConfigCodeDefinition.CODE_100012, uuid, 10);
        //获取虚拟发票号
        SysConfig sysConfig = sysConfigReadMapper.getBySysCode(SysConfigCodeDefinition.CODE_100012);
        if (sysConfigOptMapper.increment(sysConfig) <= 0) {
            throw ExceptionUtils.mpe("%s", "更新虚拟发票号失败");
        }
        redisCacheWrapper.unLock(SysConfigCodeDefinition.CODE_100012, uuid);
        //分配窗口号
        if (execDeptList.size() > 0) {
            List<Window> windowList = windowReadMapper.getWindowListByDeptCodeList(execDeptList);
            for (PrescriptionVo prescriptionVo : prescriptionVoList) {
                if ("17".equals(prescriptionVo.getPrescriptionType()) ||
                        "15".equals(prescriptionVo.getPrescriptionType()) ||
                        "16".equals(prescriptionVo.getPrescriptionType())) {
                    List<Window> filter = windowList.stream().filter(m -> m.getWindowDept().equals(prescriptionVo.getExecDeptCode()))
                            .sorted().collect(Collectors.toList());
                    if (!CollectionUtils.isEmpty(filter)) {
                        if (filter.size() > 0) {
                            Window window = filter.get(0);
                            prescriptionVo.setWindow(window.getWindow());
                            windowOptMapper.updateWindow(window);
                        }
                    }
                }
            }
        }
        //TODO 检查检验申请单申请

        //组织main
        Main main = new Main();
        main.setChargeType(ptVisit.getVisitType());
        main.setVisitId(visitId);
        main.setVisitSeries(ptVisit.getVisitSeries());
        main.setPtId(ptVisit.getPtId());
        main.setMainInvoice(sysConfig.getSysNumber().toString());
        main.setMainFee(total);
        main.setMainStatus("1");
        main.setMainChargeDeptCode(chargeDeptCode);
        main.setMainEmpCode(empCode);
        main.setMainOccType("C");
        main.setMainTime(new Date());
        main.setMainSelfpay(total);
        main.setMainAccount(new BigDecimal(0));
        main.setMainAccumulate(new BigDecimal(0));
        main.setMainCivil(new BigDecimal(0));
        main.setMainSupp(new BigDecimal(0));
        main.setMainEnterpriseSupp(new BigDecimal(0));
        //保存main
        Integer mainId = prescriptionOptMapper.savaMain(main);
        if (mainId <= 0) {
            throw ExceptionUtils.mpe("%s", "保存发票信息失败!");
        }
        //更新处方
        for (PrescriptionVo prescriptionVo : prescriptionVoList) {
            prescriptionVo.setChargeStatus("1");
            prescriptionVo.setChargeTime(new Date());
            prescriptionVo.setMainId(mainId);
            int success = prescriptionOptMapper.updatePrescriptionChargeStatus(prescriptionVo);
            if (success <= 0) {
                throw ExceptionUtils.mpe("%s", "保存发票信息失败!");
            }
            //更新明细
            for (Charge charge : prescriptionVo.getChargeList()) {
                charge.setFeeStatus("1");
                charge.setChargeTime(new Date());
                charge.setMainId(mainId);
                success = prescriptionOptMapper.updateChargeStatus(charge);
                if (success <= 0) {
                    throw ExceptionUtils.mpe("%s", "保存发票信息失败!");
                }
            }
        }
        //TODO 按理说还需要保存一个收费支付分类 目前暂不考虑

        //组织返回信息
        ChargeResultDto result = new ChargeResultDto();
        result.setFlowId(mainId.toString());
        result.setFee(main.getMainFee().toString());

        String windowGuide = ApplicationConstants.EMPTY;
        String guide = ApplicationConstants.EMPTY;
        for (PrescriptionVo vo : prescriptionVoList) {
            if ("17".equals(vo.getPrescriptionType()) || "15".equals(vo.getPrescriptionType()) || "16".equals(vo.getPrescriptionType())) {
                windowGuide += "请到" + vo.getExecDeptName() + vo.getGuideDeptAddr() + "(" + vo.getWindow() + ");";
            } else {
                guide += vo.getGuideDeptAddr() + vo.getExecDeptName() + ";";
            }
        }
        result.setWindow(windowGuide);
        result.setGuide(guide);

        return result;
    }

}
